package han.Chensing.CCirnosLibrary.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

import javax.crypto.NoSuchPaddingException;

import han.Chensing.CCirnosLibrary.func.StreamCopyCallback;
import han.Chensing.CCirnosLibrary.map.ItemDecInfo;

public class IOUtil {

    public static void copyStream(InputStream inputStream, OutputStream outputStream) throws IOException{
        copyStream(inputStream, outputStream,null);
    }

    public static void copyStream(InputStream inputStream, OutputStream outputStream,
                                  StreamCopyCallback copyCallback) throws IOException {
        byte[] bytes=new byte[1024];
        int read;
        boolean b = copyCallback != null;
        do {
            read=inputStream.read(bytes);
            if (b)
                copyCallback.onCopy(read);
            if (read!=-1)
                outputStream.write(bytes,0,read);
        }while (read!=-1);
        outputStream.flush();
    }

    public static void splitWrite(InputStream inputStream, File rootPath) throws IOException{
        int limit=1023*1024;
        byte[] bytes=new byte[1024];
        int totalRead=0;
        int num=0;
        File file=new File(rootPath,"block"+num);
        OutputStream oriOutputStream=new FileOutputStream(file);
        while (inputStream.available()>0){
            int read=inputStream.read(bytes,0,Math.min(limit-totalRead, bytes.length));
            totalRead+=read;
            oriOutputStream.write(bytes,0,read);
            if (totalRead>=limit){
                oriOutputStream.close();
                String name = "block" + (++num);
                file=new File(rootPath, name);
                oriOutputStream=new FileOutputStream(file);
                totalRead=0;
            }
        }
        oriOutputStream.close();
        File blockInfoFile=new File(rootPath,"blocks");
        FileOutputStream fileOutputStream=new FileOutputStream(blockInfoFile);
        fileOutputStream.write((num+"\n").getBytes(StandardCharsets.UTF_8));
        fileOutputStream.close();
    }

    public static String readFirstLineFromFile(File file) throws IOException{
        FileInputStream fileInputStream=new FileInputStream(file);
        InputStreamReader inputStreamReader=new InputStreamReader(fileInputStream,StandardCharsets.UTF_8);
        BufferedReader reader=new BufferedReader(inputStreamReader);
        String s = reader.readLine();
        reader.close();
        inputStreamReader.close();
        fileInputStream.close();
        return s;
    }

    public static ArrayList<String> readLinesFromFile(File file) throws IOException{
        FileInputStream fileInputStream=new FileInputStream(file);
        String line;
        ArrayList<String> map=new ArrayList<>();
        BufferedReader reader=new BufferedReader(new InputStreamReader(fileInputStream));
        while ((line=reader.readLine())!=null)
            map.add(line);
        fileInputStream.close();
        reader.close();
        return map;
    }

    public static ArrayList<String> readEncryptedLinesFromFile(File file,String key)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidAlgorithmParameterException {
        String line;
        FileInputStream fileInputStream=new FileInputStream(file);
        InputStream inputStream = AESUtil.wrapInputStream(fileInputStream, key);
        ArrayList<String> map=new ArrayList<>();
        BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
        while ((line=reader.readLine())!=null)
            map.add(line);
        reader.close();
        inputStream.close();
        fileInputStream.close();
        return map;
    }

    public static void decryptFile(File src,File dst, ItemDecInfo itemDecInfo) throws Exception{
        FileInputStream fileInputStream = null;
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream= new FileInputStream(src);
            inputStream=AESUtil.wrapInputStream(fileInputStream, itemDecInfo.getP());
            fileOutputStream = new FileOutputStream(dst);
            IOUtil.copyStream(inputStream, fileOutputStream);
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }finally {
            closeStreams(fileInputStream,fileOutputStream,inputStream);
        }
    }

    public static void encryptFile(File src,File dst, ItemDecInfo itemDecInfo) throws Exception{
        FileOutputStream fileOutputStream = null;
        OutputStream outputStream = null;
        FileInputStream fileInputStream = null;
        try {
            fileOutputStream=new FileOutputStream(dst);
            outputStream = AESUtil.wrapOutputStream(fileOutputStream,itemDecInfo.getP());
            fileInputStream=new FileInputStream(src);
            IOUtil.copyStream(fileInputStream, outputStream);
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }finally {
            closeStreams(fileInputStream,outputStream,fileOutputStream);
        }
    }

    public static void closeStreams(Object... o){
        if (o==null)
            return;
        for (Object o1 : o) {
            try{
                if (o1==null)
                    continue;
                if (o1 instanceof InputStream)
                    ((InputStream)o1).close();
                if (o1 instanceof OutputStream)
                    ((OutputStream)o1).close();
            }catch (Exception ignore){}
        }
    }
}
